﻿using System.Collections;
using System.Collections.Generic;
using MyFramework.Singleton;
using UnityEngine;
using UnityEngine.Events;

namespace MyFramework.Resource
{
    public class AssetBundleManager : SingletonAutoMono<AssetBundleManager>
    {
        /// <summary>
        /// 主包
        /// </summary>
        private UnityEngine.AssetBundle _mainAb;

        /// <summary>
        /// 依赖包获取用的配置文件
        /// </summary>
        private AssetBundleManifest _manifest;

        /// <summary>
        /// 存储加载过的AB包
        /// </summary>
        private readonly Dictionary<string, UnityEngine.AssetBundle> _abDic = new();
        
        private static string PathUrl => Application.streamingAssetsPath + "/";
        
        private static string MainAssetBundleName
        {
            get
            {
#if UNITY_IOS
            return "IOS";
#elif UNITY_ANDROID
            return "Android";
#else
                return "StandaloneWindows";
#endif
            }
        }
        
        private void LoadResource(string abName)
        {
            if (!_mainAb)
            {
                _mainAb = UnityEngine.AssetBundle.LoadFromFile(PathUrl + MainAssetBundleName);
                _manifest = _mainAb.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
            }

            // 获取依赖包相关信息
            UnityEngine.AssetBundle ab;
            string[] names = _manifest.GetAllDependencies(abName);
            for (int i = 0; i < names.Length; i++)
            {
                // 判断包是否加载过
                if (!_abDic.ContainsKey(names[i]))
                {
                    ab = UnityEngine.AssetBundle.LoadFromFile(PathUrl + names[i]);
                    _abDic.Add(names[i], ab);
                }
            }
            
            //如果没有加载过 再加载
            if (!_abDic.ContainsKey(abName))
            {
                ab = UnityEngine.AssetBundle.LoadFromFile(PathUrl + abName);
                _abDic.Add(abName, ab);
            }
        }
        
        /// <summary>
        /// 同步加载GameObject资源
        /// </summary>
        /// <param name="abName"></param>
        /// <param name="resName"></param>
        /// <returns></returns>
        public GameObject LoadGameObject(string abName, string resName)
        {
            LoadResource(abName);
            
            var obj = _abDic[abName].LoadAsset(resName);
            var go = Instantiate(obj) as GameObject;
            if (go)
            {
                return go;
            }
            
            Debug.LogError($"未找到AssetBundle：{abName}.{resName}");
            return null;
        }
        
        /// <summary>
        /// 同步加载 根据type指定类型
        /// </summary>
        /// <param name="abName"></param>
        /// <param name="resName"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public Object LoadResource(string abName, string resName, System.Type type)
        {
            LoadResource(abName);
            return _abDic[abName].LoadAsset(resName, type);
        }
        
        /// <summary>
        /// 同步加载 根据泛型指定类型
        /// </summary>
        /// <param name="abName"></param>
        /// <param name="resName"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T LoadResource<T>(string abName, string resName) where T : Object
        {
            LoadResource(abName);
            return _abDic[abName].LoadAsset<T>(resName);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="abName"></param>
        /// <param name="resName"></param>
        /// <param name="callBack"></param>
        public void LoadGameObjectAsync(string abName, string resName, UnityAction<GameObject> callBack)
        {
            StartCoroutine(LoadGameObjectCoroutine(abName, resName, callBack));
        }

        private IEnumerator LoadGameObjectCoroutine(string abName, string resName, UnityAction<GameObject> callBack)
        {
            LoadResource(abName);
            
            AssetBundleRequest abr = _abDic[abName].LoadAssetAsync(resName);
            yield return abr;

            var go = Instantiate(abr.asset) as GameObject;
            if (go)
            {
                callBack(go);
            }
            else
            {
                Debug.LogError("资源不是GameObject！");
            }
        }
        
        /// <summary>
        /// 异步加载AB包
        /// </summary>
        /// <param name="abName"></param>
        /// <param name="resName"></param>
        /// <param name="callBack"></param>
        public void LoadAssetBundleAsync(string abName, string resName, UnityAction<Object> callBack)
        {
            StartCoroutine(LoadAssetBundleCoroutine(abName, resName, callBack));
        }

        private IEnumerator LoadAssetBundleCoroutine(string abName, string resName, UnityAction<Object> callBack)
        {
            LoadResource(abName);
            
            AssetBundleRequest abr = _abDic[abName].LoadAssetAsync(resName);
            yield return abr;

            callBack(abr.asset);
        }
        
        /// <summary>
        /// 根据Type异步加载资源
        /// </summary>
        /// <param name="abName"></param>
        /// <param name="resName"></param>
        /// <param name="type"></param>
        /// <param name="callBack"></param>
        public void LoadAssetBundleAsync(string abName, string resName, System.Type type, UnityAction<Object> callBack)
        {
            StartCoroutine(LoadAssetBundleCoroutine(abName, resName, type, callBack));
        }

        private IEnumerator LoadAssetBundleCoroutine(string abName, string resName, System.Type type,
            UnityAction<Object> callBack)
        {
            LoadResource(abName);
            
            AssetBundleRequest abr = _abDic[abName].LoadAssetAsync(resName, type);
            yield return abr;

            callBack(abr.asset);
        }

        
        /// <summary>
        /// 根据泛型 异步加载资源
        /// </summary>
        /// <param name="abName"></param>
        /// <param name="resName"></param>
        /// <param name="callBack"></param>
        /// <typeparam name="T"></typeparam>
        public void LoadAssetBundleAsync<T>(string abName, string resName, UnityAction<T> callBack) where T : Object
        {
            StartCoroutine(LoadAssetBundleCoroutine<T>(abName, resName, callBack));
        }

        private IEnumerator LoadAssetBundleCoroutine<T>(string abName, string resName, UnityAction<T> callBack)
            where T : Object
        {
            LoadResource(abName);
            
            AssetBundleRequest abr = _abDic[abName].LoadAssetAsync<T>(resName);
            yield return abr;
            
            callBack(abr.asset as T);
        }
        
        /// <summary>
        /// 单个包卸载
        /// </summary>
        /// <param name="abName"></param>
        public void UnLoadAssetBundle(string abName)
        {
            if (!_abDic.TryGetValue(abName, out var ab)) return;
            ab.Unload(false);
            _abDic.Remove(abName);
        }
        
        /// <summary>
        /// 卸载所有AB包
        /// </summary>
        public void ClearAssetBundle()
        {
            UnityEngine.AssetBundle.UnloadAllAssetBundles(false);
            _abDic.Clear();
            _mainAb = null;
            _manifest = null;
        }
    }
}